/**
 * 
 */
package monster.comm.interceptor;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.executor.statement.PreparedStatementHandler;
import org.apache.ibatis.executor.statement.SimpleStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

import monster.comm.util.ReflectUtil;

/**
 * 设置分页sql和sql参数
 * @author wanghong
 * 2012-12-26
 * StatementHandlerInterceptor.java
 * 
 */
@Intercepts({  
    @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }),  
    @Signature(type = StatementHandler.class, method = "parameterize", args = { Statement.class }) 
    })  
public class StatementHandlerInterceptor extends  
AbstractStatementHandlerInterceptor implements Interceptor{
	/**
	 * <p>拦截mybatis查询语句</p>
	 * <p>如果查询语句id与{@link #getPageSqlId()}相同则拦截</p>
	 * @param invocation
	 * @return Object 
	 * @throws Throwable
	 * @see #getPageSqlId()
	 * @see # getStatementHandler()
	 */
	private Object prepare(Invocation invocation) throws Throwable {  
        StatementHandler statement = getStatementHandler(invocation);  
        MappedStatement mappedStatement=getMappedStatementHandler(statement) ;
        if (mappedStatement.getId().matches(getPageSqlId())&&(statement instanceof SimpleStatementHandler   
                || statement instanceof PreparedStatementHandler)) {  
  
               
                BoundSql boundSql = statement.getBoundSql();  
                String sql = boundSql.getSql();  
                Object parameterObject = boundSql.getParameterObject();//分页SQL<select>中parameterType属性对应的实体参数，即Mapper接口中执行分页方法的参数,该参数不得为空
                if(parameterObject==null){
                	throw new Exception("参数为空");
                }
                Map<String,Object> map = (Map)parameterObject;
                int offset = Integer.parseInt((String)map.get("offset"));
                int limit = Integer.parseInt((String)map.get("limit"));
                if (statement instanceof SimpleStatementHandler) {  
                    sql = dialect.getLimitString(sql, offset,  
                    		limit);  
                }  
                else if (statement instanceof PreparedStatementHandler) {  
                    sql = dialect.getLimitString(sql,offset > 0);  
                }  
                ReflectUtil.setFieldValue(boundSql, "sql", sql);  
            
        }  
  
        return invocation.proceed();  
    }  
      
    private Object parameterize(Invocation invocation) throws Throwable {  
        Statement statement = (Statement) invocation.getArgs()[0];  
  
        Object rtn = invocation.proceed();  
  
        if (statement instanceof PreparedStatement) {  
            PreparedStatement ps = (PreparedStatement) statement;  
  
            StatementHandler statementHandler = getStatementHandler(invocation);  
            MappedStatement mappedStatement  =getMappedStatementHandler(statementHandler) ;
  
            if (mappedStatement.getId().matches(getPageSqlId())) {  
                BoundSql boundSql = statementHandler.getBoundSql();  
                Object parameterObject = boundSql.getParameterObject();//分页SQL<select>中parameterType属性对应的实体参数，即Mapper接口中执行分页方法的参数,该参数不得为空
                if(parameterObject==null){
                	throw new Exception("参数为空");
                }
                Map<String,Object> map = (Map)parameterObject;
                List<ParameterMapping> list = boundSql.getParameterMappings(); 
                dialect.setLimitParamters(ps, list,  
                		map);  
            }  
        }  
        return rtn;  
    }  
  
    @Override  
    public Object intercept(Invocation invocation) throws Throwable {  
        Method m = invocation.getMethod();  
        if ("prepare".equals(m.getName())) {  
            return prepare(invocation);  
        }  
        else if ("parameterize".equals(m.getName())) {  
            return parameterize(invocation);  
        }  
        return invocation.proceed();  
    }  
  
    @Override  
    public Object plugin(Object target) {  
        return Plugin.wrap(target, this);  
    }  
  
    @Override  
    public void setProperties(Properties properties) {  
    }  
}
